﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XTYAPI
{
    class ModbusTCPHelper
    {
        /// <summary>
        /// 连接状态
        /// </summary>
        public bool IsConnect { get; private set; }
        public const int ReadWordCount= 125;
        public const int WriteWordCount = 123;
        public const int ReadBitCount = 2000;
        public const int WriteBitCount = 1968;
       
        object lockModbus = new object();
        Socket modbusTCP;
        short conId;
        /// <summary>
        /// 超时时间ms
        /// </summary>
        public uint TimeOut { get; set; } = 200;
        /// <summary>
        /// 连接远程服务器
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool Connect(string ip, int port)
        {
            modbusTCP = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IAsyncResult asyncResult = modbusTCP.BeginConnect(new IPEndPoint(IPAddress.Parse(ip), port), null, null);
            asyncResult.AsyncWaitHandle.WaitOne(2000);
            if (!asyncResult.IsCompleted)
            {
                modbusTCP.Close();
                IsConnect = false;
            }
            else
            {
                IsConnect = true;
            }
            return IsConnect;
        }

        public async Task<bool> AysncConnect(string ip, int port)
        {
            return await Task.Run(() => { return Connect(ip, port); });
        }
        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnect()
        {
            modbusTCP.Close();
            IsConnect = false;
        }
        /// <summary>
        /// 01功能码 读线圈 0区
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">数据地址</param>
        /// <param name="count">要读的线圈数量</param>
        /// <returns></returns>
        public bool[] ReadCoil(int deviceId, int address, int count)
        {
            /* 0xf123
             * 事务标识符(2个字节)+协议标识符(2个字节)+长度(2个字节)+单元标识符（1个字节）+Modbus报文(没有CRC)
             * H:用来表示高8位
             * L:用来表示低8位
             * 发送ADU 站地址+01+起始地址(H L)+线圈数量(H L)                
             * 响应ADU 站地址+01+字节数+内容
             */
            if (count > 2000) return null;
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法
            send[6] = (byte)deviceId;
            send[7] = 0x01;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            send[10] = (byte)(count >> 8);//获取高8位
            send[11] = (byte)(count & 0xff);//获取低8位
            int reciveCount = count / 8 + (count % 8 > 0 ? 1 : 0);//计算数据区要回复的字节数
            byte[] reciveArray = SendRecive(send, 9 + reciveCount);

            if (
               reciveArray?.Length == 9 + reciveCount &&//接收长度OK
               reciveArray[0] == send[0] && reciveArray[1] == send[1] &&//判断通信标识符是否一致
                reciveArray[6] == send[6]  //判断站地址是否一致     
                && reciveArray[7] == send[7] //判断功能码是否一致                                         
               )
            {
                byte[] data = new byte[reciveCount];
                bool[] bit = new bool[count];
                Array.Copy(reciveArray, 9, data, 0, data.Length);
                for (int i = 0; i < bit.Length; i++)
                {
                    int k = i / 8;
                    int b = i % 8;
                    bit[i] = (data[k] & (1 << b)) > 0;
                }
                return bit;
            }
            return null;
        }
        /// <summary>
        /// 01功能码 读线圈 0区异步
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">数据地址</param>
        /// <param name="count">要读的线圈数量</param>
        /// <returns></returns>
        public async Task<bool[]> AsyncReadCoil(int deviceId, int address, int count)
        {
            return await Task.Run(() => { return ReadCoil(deviceId, address, count); });
        }

        /// <summary>
        /// 02功能码 读输入 1区
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">数据地址</param>
        /// <param name="count">要读的输入数量</param>
        /// <returns></returns>
        public bool[] ReadInput(int deviceId, int address, int count)
        {
            /* 0xf123
             * H:用来表示高8位
             * L:用来表示低8位
             * 发送ADU 站地址+02+起始地址(H L)+输入数量(H L)+CRC校验(L H)               
             * 响应ADU 站地址+02+字节数+内容+CRC校验(低8位高8位)
             */
            if (count > 2000) return null;
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            send[6] = (byte)deviceId;
            send[7] = 0x02;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            send[10] = (byte)(count >> 8);//获取高8位
            send[11] = (byte)(count & 0xff);//获取低8位
            int reciveCount = count / 8 + (count % 8 > 0 ? 1 : 0);//计算数据区要回复的字节数
            byte[] reciveArray = SendRecive(send, 9 + reciveCount);

            if (
               reciveArray?.Length == 9 + reciveCount &&//接收长度OK
               reciveArray[0] == send[0] && reciveArray[1] == send[1] &&//判断通信标识符是否一致
                reciveArray[6] == send[6]  //判断站地址是否一致     
                && reciveArray[7] == send[7] //判断功能码是否一致                                         
               )
            {
                byte[] data = new byte[reciveCount];
                bool[] bit = new bool[count];
                Array.Copy(reciveArray, 9, data, 0, data.Length);
                for (int i = 0; i < bit.Length; i++)
                {
                    int k = i / 8;
                    int b = i % 8;
                    bit[i] = (data[k] & (1 << b)) > 0;
                }
                return bit;
            }
            return null;
        }
        /// <summary>
        /// 02功能码 读输入 1区 异步
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">数据地址</param>
        /// <param name="count">要读的输入数量</param>
        /// <returns></returns>
        public async Task<bool[]> AsyncReadInput(int deviceId, int address, int count)
        {
            return await Task.Run(() => { return ReadInput(deviceId, address, count); });
        }


        /// <summary>
        /// 03功能码 读保持寄存器 4区
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="count">读多少个寄存器(16位)</param>
        /// <returns></returns>
        public byte[] ReadHoldingRegister(int deviceId, int address, int count)
        {
            /*03功能码 读保持寄存器
              发送ADU 站地址+03+起始地址(高8位低8位)+读取字的数量(高8位低8位)+CRC校验(低8位高8位)
              响应ADU 站地址+03+读取字节数 内容(高8位低8位)...+CRC校验(低8位高8位)
              读取数量 一般情况下是支持125个字，但是实际的数量要根据从站的限制来设定。
              异常ADU 站地址+0x83+异常码
              */
            if (count > 125)
            {
                return null;
            }
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x03;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            send[10] = (byte)(count >> 8);//获取高8位
            send[11] = (byte)(count & 0xff);//获取低8位
            byte[] recive = SendRecive(send, 9 + count * 2);

            if (
               recive?.Length == 9 + count * 2 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致    
                )
            {
                byte[] data = new byte[count * 2];
                Array.Copy(recive, 9, data, 0, data.Length);
                return data;
            }
            return null;
        }
        /// <summary>
        /// 03功能码 读保持寄存器 4区 异步可读大于125的数量
        /// </summary>
        /// <param name="deviveId">站地址</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="count">读多少个寄存器(16位)</param>
        /// <returns></returns>
        public async Task<byte[]> AsyncReadHoldingRegister(int deviceId, int address, int count)
        {
            return await Task.Run(() =>
            {
                int errcount = 0;
                if (count > ReadWordCount)
                {
                    byte[] result = new byte[count * 2];
                    byte[] temp;
                    int k = count / 125;
                    int b = count % 125;
                    int v = k + b > 1 ? 1 : 0;
                    for (int i = 0; i < v; )
                    {
                        temp = ReadHoldingRegister(deviceId, i* ReadWordCount, ReadWordCount);
                        if (temp!=null)
                        {
                            Array.Copy(temp, 0, result,  i * ReadWordCount+ address , i <= k ? ReadWordCount : b);
                            i++;
                        }
                        else
                        {
                            errcount++;//若连续失败三次就往下继续
                            if (errcount > 3)
                            {
                                errcount = 0;
                                i++;
                            }
                        }
                    }            
                    return result;
                }
                else
                {
                    return ReadHoldingRegister(deviceId, address, count);
                }               
            });
        }

        /// <summary>
        /// 04功能码 读输入寄存器 3区
        /// </summary>
        /// <param name="deviveId"></param>
        /// <param name="address"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public byte[] ReadInputRegister(int deviceId, int address, int count)
        {
            /*04功能码 读输入寄存器
              发送ADU 站地址+04+起始地址(高8位低8位)+读取字的数量(高8位低8位)+CRC校验(低8位高8位)
              响应ADU 站地址+04+读取字节数 内容(高8位低8位)...+CRC校验(低8位高8位)
              读取数量 一般情况下是支持125个字，但是实际的数量要根据从站的限制来设定。
              异常ADU 站地址+0x84+异常码
              */
            if (count > ReadWordCount)
            {
                return null;
            }
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x04;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            send[10] = (byte)(count >> 8);//获取高8位
            send[11] = (byte)(count & 0xff);//获取低8位
            byte[] recive = SendRecive(send, 9 + count * 2);

            if (
               recive?.Length == 9 + count * 2 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致    
                )
            {
                byte[] data = new byte[count * 2];
                Array.Copy(recive, 9, data, 0, data.Length);
                return data;
            }
            return null;
        }
        /// <summary>
        /// 04功能码 读输入寄存器 3区 
        /// </summary>
        /// <param name="deviveId"></param>
        /// <param name="address"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public async Task<byte[]> AsyncReadInputRegister(int deviceId, int address, int count)
        {
            return await Task.Run(()=> 
            {
                return ReadInputRegister(deviceId, address, count);          
            });
        }
        

        /// <summary>
        /// 05功能码 写线圈 0区
        /// </summary>
        /// <param name="deviveId"></param>
        /// <param name="address"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public bool WriteCoil(int deviceId, int address, bool state)
        {
            /*发送ADU 站地址+05+起始地址(高8位低8位)+线圈状态(高8位 低8位)+CRC校验(低8位高8位)
              接收ADU 站地址+05+起始地址(高8位低8位)+线圈状态(高8位 低8位)+CRC校验(低8位高8位)
              异常ADU 站地址+0x85+异常码        
             */
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x05;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            send[10] = (byte)(state ? 0xff : 0x00);//写线圈状态
            send[11] = 0x00;

            byte[] recive = SendRecive(send, 12);

            if (
               recive?.Length == 12 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致                                         
               )
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 05功能码 写线圈 0区 异步
        /// </summary>
        /// <param name="deviveId"></param>
        /// <param name="address"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public async Task<bool> AsyncWriteCoil(int deviceId, int address, bool state)
        {
            return await Task.Run(() => { return WriteCoil(deviceId, address, state); });
        }

        /// <summary>
        /// 06功能码 写单个保持寄存器 4区
        /// </summary>
        /// <param name="deviceId">站地址</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="value">要写的值</param>
        /// <returns></returns>
        public bool WriteHoldingRegister(int deviceId, int address, short value)
        {
            /*06功能码 写保持寄存器
              发送ADU 站地址+06+寄存器地址(高8位低8位)+寄存器值(高8位 低8位)+CRC校验(低8位高8位)
              接收ADU 站地址+06+寄存器地址(高8位低8位)+寄存器值(高8位 低8位)+CRC校验(低8位高8位)
              异常ADU 站地址+0x86+异常码
             */
            byte[] send = new byte[12];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = 6;

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x06;

            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位

            byte[] bs = BitConverter.GetBytes(value);
            send[10] = bs[1];
            send[11] = bs[0];

            byte[] recive = SendRecive(send, 12);

            if (
               recive?.Length == 12 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致                                         
               )
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 06功能码 写单个保持寄存器 4区 异步
        /// </summary>
        /// <param name="deviceId">站地址</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="value">要写的值</param>
        /// <returns></returns>
        public async Task<bool> AsyncWriteHoldingRegister(int deviceId, int address, short value)
        {
            return await Task.Run(() => { return WriteHoldingRegister(deviceId, address, value); });
        }

        /// <summary>
        /// 15 功能码 写从起始地址开始的连续线圈状态 0区
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public bool WriteCoil(int deviceId, int address, bool[] state)
        {
            /*15(0x0f)功能码 写从起始地址开始的连续线圈状态
              发送ADU 站地址+0xf+起始线圈地址(高8位低8位)+线圈的数量(高8位 低8位)+线圈的字节数N+内容+CRC校验(低8位高8位)
              接收ADU 站地址+0xf+起始线圈地址(高8位低8位)+线圈的数量(高8位 低8位)+CRC校验(低8位高8位)
              异常ADU 站地址+0x8f+异常码+CRC校验(低8位高8位)
              线圈的字节数N=线圈的数量/8 如果不能整除有余数则字节数量再加1，没有用到的位用0补齐
             */
            if (state.Length > WriteBitCount) return false;

            //计算要发送的字节数量
            byte byteCount = (byte)(state.Length / 8 + (state.Length % 8 > 0 ? 1 : 0));
            byte[] send = new byte[13 + byteCount];

            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = (byte)(7 + byteCount);

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x0f;
            //线圈地址
            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位
            //线圈数量
            send[10] = (byte)(state.Length >> 8);//获取高8位
            send[11] = (byte)(state.Length & 0xff);//获取低8位

            send[12] = byteCount;
            //计算数据区 字节与bit位赋值
            for (int i = 0; i < state.Length; i++)
            {
                //0000 0000      0000 0001     0000 0011 原数据
                //0000 0001 或   0000 0010     0000 0100 位权
                //0000 0001      0000 0011     1111 1011 取反
                //                             0000 0011 原数据和取反数据进行与

                int k = i / 8;//获得对应的字节偏移量
                int b = i % 8;//获得对应的bit位偏移量
                byte byte1 = send[13 + k];//获取对应的字节
                byte byte2 = (byte)(1 << b);//获取位权
                byte1 = state[i] ? (byte)(byte1 | byte2) : (byte)(byte1 & ~byte2);//true和false处理
                send[13 + k] = byte1;//把字节还回去
            }
            byte[] recive = SendRecive(send, 12);

            if (
               recive?.Length == 12 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致                                         
               )
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 15 功能码 写从起始地址开始的连续线圈状态 0区 异步
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public async Task<bool> AsyncWriteCoil(int deviceId, int address, bool[] state)
        {
            return await Task.Run(()=> { return WriteCoil(deviceId, address, state); });
        }
            

        /// <summary>
        /// 16功能码 写从起始地址开始的连续保持寄存器 4区
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool WriteHoldingRegister(int deviceId, int address, byte[] value)
        {
            /*16(0x10)功能码 写从起始地址开始的连续保持寄存器
              发送ADU 站地址+0x10+起始寄存器地址(高8位低8位)+寄存器的数量(高8位 低8位)+寄存器的字节数N+内容+CRC校验(低8位高8位)
              接收ADU 站地址+0x10+起始寄存器地址(高8位低8位)+寄存器的数量(高8位 低8位)+CRC校验(低8位高8位)
              异常ADU 站地址+0x90+异常码+CRC校验(低8位高8位)
             */
            ushort wordCount = (ushort)(value.Length / 2);
            if (wordCount > WriteWordCount) return false;
            byte[] send = new byte[13 + wordCount * 2];


            conId++;//自增1
            byte[] id = BitConverter.GetBytes(conId);//小端的排序
            send[0] = id[1];//事务标识符 给报文起个名字
            send[1] = id[0];

            send[2] = 0;//协议标识符 0000 modbusTCP
            send[3] = 0;

            send[4] = 0;//长度字节数
            send[5] = (byte)((byte)(wordCount * 2)+7);

            //以下和ModbusRTU是一致的 只不过没有CRC校验算法

            send[6] = (byte)deviceId;
            send[7] = 0x10;
            //寄存器地址
            send[8] = (byte)(address >> 8);//获取高8位
            send[9] = (byte)(address & 0xff);//获取低8位
            //寄存器数量
            send[10] = (byte)(wordCount >> 8);//获取高8位
            send[11] = (byte)(wordCount & 0xff);//获取低8位
            //字节数量
            send[12] = (byte)(wordCount * 2);

            for (int i = 0; i < wordCount * 2; i++)
            {
                send[7+6 + i] = value[i];
            }
            byte[] recive = SendRecive(send, 6+6);
            if (
               recive?.Length == 12 &&//接收长度OK
               recive[0] == send[0] && recive[1] == send[1] &&//判断通信标识符是否一致
                recive[6] == send[6]  //判断站地址是否一致     
                && recive[7] == send[7] //判断功能码是否一致                                         
               )
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 16功能码 写从起始地址开始的连续保持寄存器 4区 异步
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> AsyncWriteHoldingRegister(int deviceId, int address, byte[] value)
        {              
            return await Task.Run(() =>
            {
                int errcount = 0;
                ushort wordCount = (ushort)(value.Length / 2);
                if (wordCount > WriteWordCount)
                {             
                    int k = wordCount / WriteWordCount;
                    int b = wordCount % WriteWordCount;
                    int v = k + b > 1 ? 1 : 0;
                    bool result = true;
                    for (int i = 0; i < v;)
                    {
                        byte[] temp = new byte[i <= k ? WriteWordCount*2 : b*2];
                        Array.Copy(value, i * WriteWordCount * 2, temp,0, temp.Length);
                         result= WriteHoldingRegister(deviceId, i * WriteWordCount+ address, temp);
                        if (result)
                        {                          
                            i++;//
                        }
                        else
                        {
                            errcount++;//若连续失败三次就往下继续
                            if (errcount > 3)
                            {
                                errcount = 0;
                                i++;
                            }
                        }
                    }
                    return result;
                }
                else
                {
                    return WriteHoldingRegister(deviceId, address, value);
                }
            });
        }

        /// <summary>
        /// 16功能码 写32位整形保持寄存器
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool WriteHoldingRegister(int deviceId, int address, int value)
        {
            byte[] bs = BitConverter.GetBytes(value);
            byte[] send = new byte[4];
            send[0] = bs[3];
            send[1] = bs[2];
            send[2] = bs[1];
            send[3] = bs[0];
            return WriteHoldingRegister(deviceId, address, send);
        }
        /// <summary>
        /// 16功能码 写32位整形保持寄存器
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> AsyncWriteHoldingRegister(int deviceId, int address, int value)
        {
            return await Task.Run(()=> { return WriteHoldingRegister(deviceId, address, value); });
        }

        /// <summary>
        /// 16功能码 写32位浮点保持寄存器
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool WriteHoldingRegister(int deviceId, int address, float value)
        {
            byte[] bs = BitConverter.GetBytes(value);
            byte[] send = new byte[4];
            send[0] = bs[3];
            send[1] = bs[2];
            send[2] = bs[1];
            send[3] = bs[0];
            return WriteHoldingRegister(deviceId, address, send);
        }
        public async Task<bool> AsyncWriteHoldingRegister(int deviceId, int address, float value)
        {
            return await Task.Run(()=> {return WriteHoldingRegister(deviceId, address, value); });
        }
        /// <summary>
        /// 发送与接收
        /// </summary>
        /// <param name="array"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        private byte[] SendRecive(byte[] array, int count)
        {
            try
            {
                Monitor.Enter(lockModbus);
                modbusTCP.Send(array);
                int timeOut = 0;
                while (modbusTCP.Available < count && timeOut < TimeOut)
                {
                    Thread.Sleep(1);
                    timeOut++;
                }
                if (timeOut >= TimeOut || modbusTCP.Available == 0)
                {
                    return null;
                }
                byte[] resByte = new byte[modbusTCP.Available];               
                modbusTCP.Receive(resByte);
                return resByte;
            }
            catch (Exception)
            {

                return null;
            }
            finally
            {
                Monitor.Exit(lockModbus);
            }
        }
    }
}
